﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
using System.Security.Permissions;
using Microsoft.Win32.SafeHandles;

namespace Goldlight.Mvvm.Base.Infrastructure
{
    public static class NativeMethods
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        internal static extern int GetModuleFileName(HandleRef hModule, StringBuilder buffer, int length);

        private static string startupPath;
        private static HandleRef NullHandleRef = new HandleRef();

        public static string StartupPath()
        {
            if (startupPath == null)
            {
                StringBuilder buffer = new StringBuilder(260);
                GetModuleFileName(NullHandleRef, buffer, buffer.Capacity);
                startupPath = Path.GetDirectoryName(buffer.ToString());
            }
            new FileIOPermission(FileIOPermissionAccess.PathDiscovery, startupPath).Demand();
            return startupPath;
        }

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool DeleteFileTransacted(
            [MarshalAs(UnmanagedType.LPWStr)]string file,
            IntPtr transaction);

        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern SafeFileHandle CreateFileTransacted(
            [MarshalAs(UnmanagedType.LPWStr)]string fileName,
            [MarshalAs(UnmanagedType.U4)] NativeMethods.FileAccess fileAccess,
            [MarshalAs(UnmanagedType.U4)] NativeMethods.FileShare fileShare,
            IntPtr securityAttributes,
            [MarshalAs(UnmanagedType.U4)] NativeMethods.FileMode creationDisposition,
            [MarshalAs(UnmanagedType.U4)] uint flags,
            IntPtr template,
            IntPtr transaction,
            IntPtr miniVersion,
            IntPtr extendedOpenInformation);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern SafeFileHandle CreateFile(
            [MarshalAs(UnmanagedType.LPWStr)]string fileName,
            [MarshalAs(UnmanagedType.U4)] NativeMethods.FileAccess dwDesiredAccess,
            [MarshalAs(UnmanagedType.U4)] NativeMethods.FileShare dwShareMode,
            IntPtr securityAttributes,
            [MarshalAs(UnmanagedType.U4)] NativeMethods.FileMode dwCreationDisposition,
            [MarshalAs(UnmanagedType.U4)] uint dwFlagsAndAttributes,
            IntPtr hTemplateFile);

        /// <summary>
        /// Moves an existing file or a directory, including its children, as a transacted operation.
        /// </summary>
        /// <param name="existingFileName">The current name of the existing file or directory on the local computer. </param>
        /// <param name="newFileName">The new name for the file or directory. The new name must not already exist. 
        /// A new file may be on a different file system or drive. A new directory must be on the same drive.</param>
        /// <param name="progressRoutine">The <see cref="CopyProgressCallback"/> called each time
        /// a portion of the file has been copied.</param>
        /// <param name="data">The argument to be passed to the callback function. This parameter can 
        /// be NULL.</param>
        /// <param name="copyFlags">Flags that specify how the file is to be copied.</param>
        /// <param name="transaction">A handle to the transaction.</param>
        /// <returns>True if the function succeeds, false otherwise.</returns>
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool MoveFileTransacted(
            [MarshalAs(UnmanagedType.LPWStr)]string existingFileName,
            [MarshalAs(UnmanagedType.LPWStr)]string newFileName,
            CopyProgressCallback progressRoutine,
            IntPtr data,
            MoveFileFlags moveFlags,
            IntPtr transaction);

        /// <summary>
        /// Moves an existing file or a directory, including its children, as a transacted operation.
        /// </summary>
        /// <param name="existingFileName">The current name of the existing file or directory on the local computer. </param>
        /// <param name="newFileName">The new name for the file or directory. The new name must not already exist. 
        /// A new file may be on a different file system or drive. A new directory must be on the same drive.</param>
        /// <param name="progressRoutine">The <see cref="CopyProgressCallback"/> called each time
        /// a portion of the file has been copied.</param>
        /// <param name="data">The argument to be passed to the callback function. This parameter can 
        /// be NULL.</param>
        /// <param name="copyFlags">Flags that specify how the file is to be copied.</param>
        /// <returns>True if the function succeeds, false otherwise.</returns>
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool MoveFile(
            [MarshalAs(UnmanagedType.LPWStr)]string existingFileName,
            [MarshalAs(UnmanagedType.LPWStr)]string newFileName,
            CopyProgressCallback progressRoutine,
            IntPtr data,
            MoveFileFlags moveFlags);

        /// <summary>
        /// Callback routine, called during <see cref="CopyFileTransacted"/>
        /// </summary>
        /// <param name="totalFileSize">The total size of the file, in bytes.</param>
        /// <param name="totalBytesTransferred">The total number of bytes transferred 
        /// from the source file to the destination file since the copy operation began.</param>
        /// <param name="streamSize">The total size of the current file stream, in bytes.</param>
        /// <param name="streamBytesTransferred">The total number of bytes in the current stream 
        /// that have been transferred from the source file to the destination file since the copy 
        /// operation began.
        /// </param>
        /// <param name="streamNumber">A handle to the current stream. The first time 
        /// CopyProgressCallback is called, the stream number is 1.
        /// </param>
        /// <param name="callbackReason">The reason that CopyProgressCallback was called.</param>
        /// <param name="sourceFile">A handle to the source file.</param>
        /// <param name="destinationFile">A handle to the destination file.</param>
        /// <param name="data">Argument passed in.</param>
        /// <returns>One of <see cref="CopyProgressResult"/>.</returns>
        public delegate CopyProgressResult CopyProgressCallback(
            long totalFileSize,
            long totalBytesTransferred,
            long streamSize,
            long streamBytesTransferred,
            uint streamNumber,
            CopyProgressCallbackReason callbackReason,
            IntPtr sourceFile,
            IntPtr destinationFile,
            IntPtr data);

        /// <summary>
        /// Copies an existing file to a new file as a transacted operation, 
        /// notifying the application of its progress through a callback function.
        /// </summary>
        /// <param name="existingFileName">The name of an existing file. </param>
        /// <param name="newFileName">The name of the new file. </param>
        /// <param name="progressRoutine">The <see cref="CopyProgressCallback"/> called each time
        /// a portion of the file has been copied.</param>
        /// <param name="data">The argument to be passed to the callback function. This parameter can 
        /// be NULL.</param>
        /// <param name="cancel">If this flag is set to TRUE during the copy operation, the operation 
        /// is canceled. Otherwise, the copy operation will continue to completion.</param>
        /// <param name="copyFlags">Flags that specify how the file is to be copied.</param>
        /// <param name="transaction">A handle to the transaction.</param>
        /// <returns>True if the function succeeds, false otherwise.</returns>
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CopyFileTransacted(
            [MarshalAs(UnmanagedType.LPWStr)]string existingFileName,
            [MarshalAs(UnmanagedType.LPWStr)]string newFileName,
            CopyProgressCallback progressRoutine,
            IntPtr data,
            ref Int32 cancel,
            CopyFileFlags copyFlags,
            IntPtr transaction);

        /// <summary>
        /// Copies an existing file to a new file notifying the application of its 
        /// progress through a callback function.
        /// </summary>
        /// <param name="existingFileName">The name of an existing file. </param>
        /// <param name="newFileName">The name of the new file. </param>
        /// <param name="progressRoutine">The <see cref="CopyProgressCallback"/> called each time
        /// a portion of the file has been copied.</param>
        /// <param name="data">The argument to be passed to the callback function. This parameter can 
        /// be NULL.</param>
        /// <param name="cancel">If this flag is set to TRUE during the copy operation, the operation 
        /// is canceled. Otherwise, the copy operation will continue to completion.</param>
        /// <param name="copyFlags">Flags that specify how the file is to be copied.</param>
        /// <returns>True if the function succeeds, false otherwise.</returns>
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CopyFile(
            [MarshalAs(UnmanagedType.LPWStr)]string existingFileName,
            [MarshalAs(UnmanagedType.LPWStr)]string newFileName,
            CopyProgressCallback progressRoutine,
            IntPtr data,
            ref Int32 cancel,
            CopyFileFlags copyFlags);

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool RemoveDirectoryTransacted(
            [MarshalAs(UnmanagedType.LPWStr)]string path,
            IntPtr transaction);

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CreateDirectoryTransacted(
            [MarshalAs(UnmanagedType.LPWStr)]string templateDirectory,
            [MarshalAs(UnmanagedType.LPWStr)]string newDirectory,
            IntPtr sam,
            IntPtr transaction);

        [Flags]
        public enum MoveFileFlags
        {
            MOVEFILE_REPLACE_EXISTING = 0x00000001,
            MOVEFILE_COPY_ALLOWED = 0x00000002,
            MOVEFILE_DELAY_UNTIL_REBOOT = 0x00000004,
            MOVEFILE_WRITE_THROUGH = 0x00000008,
            MOVEFILE_CREATE_HARDLINK = 0x00000010,
            MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020
        }

        public enum CopyProgressResult : uint
        {
            PROGRESS_CONTINUE = 0,
            PROGRESS_CANCEL = 1,
            PROGRESS_STOP = 2,
            PROGRESS_QUIET = 3
        }

        public enum CopyProgressCallbackReason : uint
        {
            CALLBACK_CHUNK_FINISHED = 0x00000000,
            CALLBACK_STREAM_SWITCH = 0x00000001
        }

        [Flags]
        public enum CopyFileFlags : uint
        {
            COPY_FILE_FAIL_IF_EXISTS = 0x00000001,
            COPY_FILE_RESTARTABLE = 0x00000002,
            COPY_FILE_OPEN_SOURCE_FOR_WRITE = 0x00000004,
            COPY_FILE_ALLOW_DECRYPTED_DESTINATION = 0x00000008
        }

        [Flags]
        public enum EFileAttributes : uint
        {
            Readonly = 0x00000001,
            Hidden = 0x00000002,
            System = 0x00000004,
            Directory = 0x00000010,
            Archive = 0x00000020,
            Device = 0x00000040,
            Normal = 0x00000080,
            Temporary = 0x00000100,
            SparseFile = 0x00000200,
            ReparsePoint = 0x00000400,
            Compressed = 0x00000800,
            Offline = 0x00001000,
            NotContentIndexed = 0x00002000,
            Encrypted = 0x00004000,
            Write_Through = 0x80000000,
            Overlapped = 0x40000000,
            NoBuffering = 0x20000000,
            RandomAccess = 0x10000000,
            SequentialScan = 0x08000000,
            DeleteOnClose = 0x04000000,
            BackupSemantics = 0x02000000,
            PosixSemantics = 0x01000000,
            OpenReparsePoint = 0x00200000,
            OpenNoRecall = 0x00100000,
            FirstPipeInstance = 0x00080000
        }

        [Flags]
        public enum FileShare
        {
            FILE_SHARE_NONE = 0x00,
            FILE_SHARE_READ = 0x01,
            FILE_SHARE_WRITE = 0x02,
            FILE_SHARE_DELETE = 0x04
        }
        public enum FileMode
        {
            CREATE_NEW = 1,
            CREATE_ALWAYS = 2,
            OPEN_EXISTING = 3,
            OPEN_ALWAYS = 4,
            TRUNCATE_EXISTING = 5
        }
        public enum FileAccess
        {
            GENERIC_READ = unchecked((int)0x80000000),
            GENERIC_WRITE = 0x40000000
        }
    }
}
